/* save 20/12/07(月) 21:38:25 次回は test()を作ってあるので、フィードバック的にやってみる。 3点目を0,10とかでやっているが、これを10,10で計算し直し。 20/12/05(土) 22:05:47 今回は 次回は 頂点の位置を始点からの相対高さで指定したい。 aがいくつだったら頂点のyを指定の値にできるか。 */ class App { constructor( canvasId ) { this.cc = document.getElementById( canvasId ).getContext( "2d" ); let cc = this.cc; cc.translate( cc.canvas.width / 2, cc.canvas.height /2 ); cc.scale( 1, -1 ); cc.tmp = { circle : function( x, y, r, strokeStyle, fillStyle ) { cc.beginPath(); cc.arc( x, y, r, 0, 6.28 ); cc.closePath(); if( typeof fillStyle !== "undefined" && fillStyle != null ) { cc.fillStyle = fillStyle; cc.fill(); } if( typeof strokeStyle !== "undefined" && strokeStyle != null ) { cc.strokeStyle = strokeStyle; cc.stroke(); } }, } this.graphPaper = new GraphPaper(); this.draw( this.cc ); this.anms = new Array(); this.anms.push( { object : this.graphPaper, propertyName : "sy", sx : -100, ex : 100, step : 5, dir : -1, frame : function() { this.object[ this.propertyName ] += this.step * this.dir; //check. if( this.dir > 0 && this.object[ this.propertyName ] >= this.ex || this.dir < 0 && this.object[ this.propertyName ] <= this.sx ) { this.dir *= -1; } }, } ); this.anms.push( { object : this.graphPaper, propertyName : "ey", sx : -100, ex : 100, step : 5, dir : 1, frame : function() { this.object[ this.propertyName ] += this.step * this.dir; //check. if( this.dir > 0 && this.object[ this.propertyName ] >= this.ex || this.dir < 0 && this.object[ this.propertyName ] <= this.sx ) { this.dir *= -1; } }, } ); this.anms.push( { object : this.graphPaper, propertyName : "sx", sx : -200, ex : 100, step : 5, dir : 1, frame : function() { this.object[ this.propertyName ] += this.step * this.dir; //check. if( this.dir > 0 && this.object[ this.propertyName ] >= this.ex || this.dir < 0 && this.object[ this.propertyName ] <= this.sx ) { this.dir *= -1; } }, } ); this.anms.push( { object : this.graphPaper, propertyName : "ex", sx : -100, ex : 200, step : 5, dir : -1, frame : function() { this.object[ this.propertyName ] += this.step * this.dir; //check. if( this.dir > 0 && this.object[ this.propertyName ] >= this.ex || this.dir < 0 && this.object[ this.propertyName ] <= this.sx ) { this.dir *= -1; } }, } ); }//constructor start() { this.runtype = "timer"; switch( this.runtype ) { case "key": this.keyfunc = function( e ) { switch( e.which ) { case 32: this.frame(); break; case 65: //a this.graphPaper.a += 0.001; this.draw( this.cc ); break; case 90: //z this.graphPaper.a -= 0.001; this.draw( this.cc ); break; default: console.log( e.which ); return true; } e.stopPropagation(); e.preventDefault(); return false; }.bind( this ); addEventListener( "keydown", this.keyfunc ); break; case "timer": default: this.timerId = setInterval( this.frame.bind( this ), 100 ); }//switch }//start() stop() { switch( this.runtype ) { case "key": removeEventListener( "keydown", this.keyfunc ); break; case "timer": clearInterval( this.timerId ); break; } } frame() { this.anms.map( anm => anm.frame() ); this.draw( this.cc ); } draw( cc ) { this.graphPaper.draw( cc ); //原点 if( 0 ) { cc.tmp.circle( 0, 0, 4, null, "red" ); cc.save(); cc.scale( 1, -1 ); cc.fillText( "原点", 12, 0 ); cc.restore(); } } }//class App class GraphPaper { constructor() { this.f = null; this.sx = -100; this.sy = 10; this.ex = 100; this.ey = 50; this.numberOfPoints = 100; this.adjustY = null; } //sxからexまでをnumberOfPoints分割したとき、 //分割のindex番目の、グラフ上のpx,pyを求める。 setIndex( index ) { this.index = index; this.px = this.sx + index * this.step; this.py = this.f( this.px ); } //放物線の式を更新 update( sx, sy, ex, ey ) { this.maxY = Math.max( sy, ey ) + 40; //https://www.wolframalpha.com/input/?i=10+%3D+%28-100%29%5E2a-100B+%2B+c%2C50+%3D+100%5E2a+%2B+100B+%2B+c%2Cc+%3D+90+%2B+B%5E2%2F+%284a%29&lang=ja //y=ax^2 + bx + c //sy = a sx^2 + b sx + c [1] //ey = a ex^2 + b ex + c [2] //cy = -a( b / 2a )^2 + c [3] let cy = this.maxY; let r = sx - ex; let u = sx * sx - ex * ex; let t = sy - ey; let A = -4 * ex * ex * r * r + 4 * sx * sx * ex * r - 4 * ex * ex * ex * r - u * u; let B = 4 * ey * r * r - 4 * ex * r * t - 4 * cy * r * r + 2 * t * u; let C = - t * t; let a = ( -B + Math.sqrt( B * B - 4 * A * C ) ) / ( 2 * A ); let b = ( t - a * u ) / r; let c = ey - a * ex * ex - b * ex; // console.log( a, b, c ); this.f = function( x ) { return a * Math.pow( x, 2 ) + b * x + c; } //sxからexまでをnumberOfPoints分割するときの間隔 this.step = ( this.ex - this.sx ) / this.numberOfPoints; //グラフの位置調整 this.setIndex( 0 ); this.adjustY = this.sy - this.py; }//update() draw( cc ) { //画面クリア cc.clearRect( -cc.canvas.width / 2, -cc.canvas.height / 2, cc.canvas.width, cc.canvas.height ); //放物線の式を更新 this.update( this.sx, this.sy, this.ex, this.ey ); //放物線を描く for( let i = 0; i <= this.numberOfPoints; i++ ) { this.setIndex( i ); let y = this.py + this.adjustY; cc.tmp.circle( this.px, y, 2, null, "blue" ); } cc.fillStyle = "black"; //始点 cc.tmp.circle( this.sx, this.sy, 8, "blue" ); cc.save(); cc.translate( this.sx + 12, this.sy ); cc.scale( 1, -1 ); cc.fillText( "始点", 0, 0 ); cc.restore(); //終点 cc.tmp.circle( this.ex, this.ey, 8, "red" ); cc.save(); cc.translate( this.ex + 12, this.ey ); cc.scale( 1, -1 ); cc.fillText( "終点", 0, 0 ); cc.restore(); //参考値 if( 1 ) { cc.fillStyle = "red"; cc.fillRect( -cc.canvas.width / 2, this.maxY, cc.canvas.width, 1 ); cc.save(); cc.translate( -cc.canvas.width / 2 + 16, this.maxY ); cc.scale( 1, -1 ); cc.fillText( "この高さに放物線の頂点を合わせたい。", 0, -3 ); cc.restore(); } }//draw() }//class GraphPaper